/**
 * MIT License
 *
 * Copyright (c) 2024 lishengbo
 * Email: 397237738@qq.com
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.cobertura.instrument;

import lsieun.classfile.ClassFile;
import lsieun.utils.ReadUtils;
import org.cobertura.test.util.PathUtil;
import org.junit.Test;
import lsieun.classfile.Attributes;
import lsieun.classfile.ConstantPool;
import lsieun.classfile.MethodInfo;
import lsieun.classfile.attrs.Code;
import lsieun.classfile.attrs.LocalVariableTable;
import lsieun.classfile.attrs.LocalVariableTypeTable;
import lsieun.code.Instruction;
import lsieun.code.utils.InstructionChain;
import lsieun.code.visitors.StandardOpcodeVisitor;
import lsieun.code.visitors.OpcodeReadVisitor;
import lsieun.utils.*;
import lsieun.vs.*;

/**
 * 偏移量对应关系说明 举例：
 * 0000: iload_1              // 1B
 * 0001: ifeq            14   // 99000E
 * 0004: getstatic       #2   // B20002     || java/lang/System.out:Ljava/io/PrintStream;
 * 0007: ldc             #3   // 1203       || value is true
 * 0009: invokevirtual   #4   // B60004     || java/io/PrintStream.println:(Ljava/lang/String;)V
 * 0012: goto            11   // A7000B
 * 0015: getstatic       #2   // B20002     || java/lang/System.out:Ljava/io/PrintStream;
 * 0018: ldc             #5   // 1205       || value is false
 * 0020: invokevirtual   #4   // B60004     || java/io/PrintStream.println:(Ljava/lang/String;)V
 * 0023: return               // B1
 *
 * 0000到0001，只增长1，是因为1B这个指令只占了一个字节
 * 0001到0004 增长了3，1+3=4，是因为99000E占了三个字节
 */
public class PrintCodeInfo {
    @Test
    public void printCodeInfo(){
        String beforeClass= PathUtil.getClassesPath("/org/cobertura/test/LabelProbeTest.class");
        String name_and_type = "test:(Z)V";

        byte[] bytes = ReadUtils.readByPath(beforeClass);

        // 第三步，处理数据
        ClassFile classfile = ClassFile.parse(bytes);
        process(classfile, name_and_type);
    }

    public static void process(ClassFile classfile, String name_and_type) {
        // constant_pool
        ConstantPool cp = classfile.constant_pool;
        ConstantPoolUtils.print(cp);
        System.out.println("=== === ===  === === ===  === === ===");

        // method_info
        MethodInfo method_info = MethodUtils.findMethod(classfile, name_and_type);
        MethodUtils.print(method_info, cp);
        System.out.println("=== === ===  === === ===  === === ===");

        // Code Attribute
        Code code_attr = AttributeUtils.findCodeAttribute(method_info);
        HumanReadableVisitor human_readable_visitor = new HumanReadableVisitor(cp);
        code_attr.accept(human_readable_visitor);
        System.out.println("=== === ===  === === ===  === === ===");

        // opcode
        byte[] code_bytes = code_attr.code;
        OpcodeReadVisitor rv = new OpcodeReadVisitor(code_bytes);
        InstructionChain chain = rv.getInstructionChain();

        StandardOpcodeVisitor v = new StandardOpcodeVisitor(cp, code_bytes);
        Instruction current = chain.start;
        while (current != null) {
            current.accept(v);
            current = current.next;
        }
        System.out.println("=== === ===  === === ===  === === ===");

        // attributes
        Attributes attributes = code_attr.attributes;
        LocalVariableTable localVariableTable = (LocalVariableTable) AttributeUtils.findAttribute(attributes, "LocalVariableTable");
        if (localVariableTable != null) {
            localVariableTable.accept(human_readable_visitor);
        }

        LocalVariableTypeTable localVariableTypeTable = (LocalVariableTypeTable) AttributeUtils.findAttribute(attributes, "LocalVariableTypeTable");
        if (localVariableTypeTable != null) {
            localVariableTypeTable.accept(human_readable_visitor);
        }
    }
}
